package com.hxl.tech.gateway.auth.oauth;

import static org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus.APPROVED;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.oauth2.provider.approval.Approval;
import org.springframework.security.oauth2.provider.approval.Approval.ApprovalStatus;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.util.Assert;


/**
 * 授权信息存储实现
 * @author soliddq
 * @date 2023-10-14
 */
public class MyJdbcApprovalStore implements ApprovalStore {

    private final JdbcTemplate jdbcTemplate;

    private final Log logger = LogFactory.getLog(getClass());

    private final RowMapper<Approval> rowMapper = new AuthorizationRowMapper();

    private static final String TABLE_NAME = "application_oauth";

    private static final String FIELDS = "merchant_code, app_id, scope, status, expires_time, update_time";

    private static final String WHERE_KEY = "where merchant_code=? and app_id=?";

    private static final String DEFAULT_ADD_APPROVAL_STATEMENT = String.format("insert into %s ( %s ) values (?,?,?,?,?,?)", TABLE_NAME, FIELDS);

    private static final String DEFAULT_REFRESH_APPROVAL_STATEMENT = String.format("update %s set expires_time=?, status=?, update_time=? " + WHERE_KEY, TABLE_NAME);

    private static final String DEFAULT_GET_APPROVAL_SQL = String.format("select %s from %s " + WHERE_KEY, FIELDS, TABLE_NAME);

    private String addApprovalStatement = DEFAULT_ADD_APPROVAL_STATEMENT;

    private String refreshApprovalStatement = DEFAULT_REFRESH_APPROVAL_STATEMENT;

    private  String findApprovalStatement = DEFAULT_GET_APPROVAL_SQL;

    private boolean handleRevocationsAsExpiry = false;

    public MyJdbcApprovalStore(DataSource dataSource) {
        Assert.notNull(dataSource);
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public void setHandleRevocationsAsExpiry(boolean handleRevocationsAsExpiry) {
        this.handleRevocationsAsExpiry = handleRevocationsAsExpiry;
    }

    /**
     * 获取授权信息
     * @param merchantCode
     * @param appId
     * @return
     */
    @Override
    public List<Approval> getApprovals(String merchantCode, String appId) {
        return jdbcTemplate.query(findApprovalStatement, rowMapper, merchantCode, appId);
    }

    /**
     * 新增或更新授权信息
     * @param approvals
     * @return
     */
    @Override
    public boolean addApprovals(final Collection<Approval> approvals) {
        boolean success = true;
        for (Approval approval : approvals) {
            if (!updateApproval(refreshApprovalStatement, approval)) {
                if (insertApproval(addApprovalStatement, approval)) {

                } else {
                    success = false;
                }
            }
        }
        return success;
    }

    /**
     * 回收授权
     * @param approvals
     * @return
     */
    @Override
    public boolean revokeApprovals(Collection<Approval> approvals) {

        return true;
    }

    /**
     * 字段转换
     */
    private static class AuthorizationRowMapper implements RowMapper<Approval> {
        @Override
        public Approval mapRow(ResultSet rs, int rowNum) throws SQLException {
            String merchantCode = rs.getString(1);
            String appId = rs.getString(2);
            String scope = rs.getString(3);
            String status = rs.getString(4);
            Date expiresTime = rs.getTimestamp(5);
            Date updateTime = rs.getTimestamp(6);
            return new Approval(merchantCode, appId, scope, expiresTime, ApprovalStatus.valueOf(status), updateTime);
        }
    }

    /**
     * 插入授权信息
     * @param sql
     * @param approval
     * @return
     */
    private boolean insertApproval(final String sql, final Approval approval) {
        logger.info(String.format("refreshing approval: [%s]", approval));
        int refreshed = jdbcTemplate.update(sql, ps -> {
            ps.setString(1, approval.getUserId());
            ps.setString(2, approval.getClientId());
            ps.setString(3, approval.getScope());
            ps.setString(4, (approval.getStatus() == null ? APPROVED : approval.getStatus()).toString());
            ps.setTimestamp(5, new Timestamp(approval.getExpiresAt().getTime()));
            ps.setTimestamp(6, new Timestamp(approval.getLastUpdatedAt().getTime()));
        });
        return refreshed == 1;
    }

    /**
     * 更新授权信息
     * @param sql
     * @param approval
     * @return
     */
    private boolean updateApproval(final String sql, final Approval approval) {
        int refreshed = jdbcTemplate.update(sql, ps -> {
            ps.setTimestamp(1, new Timestamp(approval.getExpiresAt().getTime()));
            ps.setString(2, (approval.getStatus() == null ? APPROVED : approval.getStatus()).toString());
            ps.setTimestamp(3, new Timestamp(approval.getLastUpdatedAt().getTime()));
            ps.setString(4, approval.getUserId());
            ps.setString(5, approval.getClientId());
        });
        return refreshed == 1;
    }

}
